package gbench.sandbox.tuple;

import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.*;
import org.junit.jupiter.api.Test;

/**
 * 
 * @author gbench
 *
 */
public class JunitM2 {
    
    /**
     * 基础元组
     * @author gbench
     *
     * @param <T> 第一元素类型
     * @param <U> 第二元素类型
     */
    public static class Tuple2<T,U>{
        
        /**
         * 元组构造
         * @param t
         * @param u
         */
        public Tuple2(T t,U u) {
            this._1 = t;
            this._2 = u;
        }
        
        public final T _1;
        public final U _2;
    }
    
    /**
     * LISP的元素类型：基本元素节点类型
     * @author gbench
     * @param <T> 元素数据类型
     */
    public static final class Elem<T> extends Tuple2<T,Elem<T>>{
        /**
         * 带有父节点的恶狗
         * @param t
         * @param parent 父节点
         */
        public Elem(final T t, final Elem<T> parent) {
            super(t, parent);
        }
        
        /**
         * 没有父节点的构造
         * @param t 元素数据
         */
        public Elem(final T t) {
            super(t, null);
        }
        
        /**
         * 读取路径数据
         * @param <U> 参数类型
         * @param elem 提取一个元组元素的路径信息。
         * @return 路径数据数组
         */
        public static <U> List<U> data( final Elem<U> elem ){
            if(elem._2!=null) {// 非根节点
                final var p_data = data(elem._2);// 提取父节点元素数据
                p_data.add(elem._1);// 把当前节点元素数据补充到父节点数据形成 完整的节点数据
                return p_data;
            } else {// 根节点
                return elem == null
                ? new ArrayList<>() // 空节点 返回空链表
                : Stream.of(elem._1).collect(Collectors.toList());// 形成链表数据
            }// if
        }
        
        /**
         * 路径元素
         * @return 路径元素
         */
        public List<T> data(){
            return data(this);
        }
    }
    
    /**
     * 
     * @param <T>
     * @param ts
     * @return 数组元素
     */
    @SafeVarargs
    public static <T> T[] A( final T  ...ts) {
        return ts;
    }
    
    /**
     * 
     * @param <T>
     * @param ts
     * @return 列表元素
     */
    @SafeVarargs
    public static <T> List<T> L( final T  ...ts) {
        return Arrays.asList(ts);
    }

    /**
     * 全排列组合
     * 
     * @param <T> 源数据元素类型
     * @param sss 数据元素列表:源数据列表
     * @return 生成一个全排列组合
     */
    public static <T> Stream<Elem<T>> cph(final List<T[]> sss) {
        return sss.size() < 2
        ? Stream.of(sss.get(0)).map(Elem::new)// 根节点
        : cph(sss.subList(0, sss.size() - 1)).parallel() // 非根节点
          .flatMap( parent -> Stream.of(sss.get(sss.size() - 1)).map( t -> new Elem<>(t, parent) ) );// 在福建
    }
    
    /**
    * 全排列组合
    * 
    * @param <T> 源数据元素类型
    * @param sss 数据元素列表:源数据列表
    * @return 生成一个全排列组合
    */
   @SafeVarargs
   public static <T> Stream<Elem<T>> cph(final T[]... sss){
       return cph(Arrays.asList(sss));
   }
    
    /**
     * 全排列组合:项目结合
     * 
     * @param <T> 源数据元素类型
     * @param sss 数据元素列表:源数据列表
     * @return 生成一个全排列组合
     */
    public static <T> Stream<List<T>> cph2(final List<T[]> sss) {
       return cph(sss).map(e->e.data());
    }
    
    /**
     * 全排列组合:项目结合
     * 
     * @param <T> 源数据元素类型
     * @param sss 数据元素列表:源数据列表
     * @return 生成一个全排列组合
     */
    @SafeVarargs
    public static <T> Stream<List<T>> cph2(final T[]... sss) {
       return cph(sss).map(e->e.data());
    }
    
    /**
     * 测试程序
     */
    @Test
    public void foo() {
        final var n1 = new Elem<>(1);
        final var n2 = new Elem<>(2,n1);
        final var n3 = new Elem<>(3,n2);
        System.out.println(n3.data());
        
        final var ai = new AtomicInteger(0);
        final var digits = A(0,1,2,3,4,5,6,7,8,9);
        cph2(digits,digits,digits,digits,digits,digits,digits,digits,digits)
        .filter(e->e.stream().collect(Collectors.summarizingInt(v->v)).getSum()==5)
        .forEach(System.out::println);
        System.out.println(MessageFormat.format("total:{0}", ai.get()));
        
        final var letters = "abcdefg".split("");
        cph2(L("ABC".split(""),letters,letters,letters,letters)).forEach(System.out::println);
    }

}
